Proper connect_port
[juce-lv2.git] / juce / source / extras / the jucer / src / model / jucer_JucerDocument.cpp
blob69ec2571404f13f3b167383f1b134f8ea2fb0ba5
1 /*
2 ==============================================================================
4 This file is part of the JUCE library - "Jules' Utility Class Extensions"
5 Copyright 2004-11 by Raw Material Software Ltd.
7 ------------------------------------------------------------------------------
9 JUCE can be redistributed and/or modified under the terms of the GNU General
10 Public License (Version 2), as published by the Free Software Foundation.
11 A copy of the license is included in the JUCE distribution, or can be found
12 online at www.gnu.org/licenses.
14 JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
15 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
16 A PARTICULAR PURPOSE. See the GNU General Public License for more details.
18 ------------------------------------------------------------------------------
20 To release a closed-source product which uses JUCE, commercial licenses are
21 available: visit www.rawmaterialsoftware.com/juce for more information.
23 ==============================================================================
26 #include "../jucer_Headers.h"
27 #include "jucer_JucerDocument.h"
28 #include "jucer_ObjectTypes.h"
29 #include "../ui/jucer_TestComponent.h"
31 const char* const defaultClassName = "NewJucerComponent";
32 const char* const defaultParentClasses = "public Component";
34 static const int timerInterval = 150;
36 //==============================================================================
37 JucerDocument::JucerDocument()
38 : FileBasedDocument (".cpp", "*.cpp",
39 "Open a Jucer C++ file...",
40 "Save as a Jucer C++ file..."),
41 className (defaultClassName),
42 parentClasses (defaultParentClasses),
43 fixedSize (false),
44 initialWidth (600),
45 initialHeight (400),
46 snapGridPixels (8),
47 snapActive (true),
48 snapShown (true),
49 lastFocusedComp (0),
50 lastClickCounter (0),
51 componentOverlayOpacity (0.33f)
53 resources.setDocument (this);
55 startTimer (timerInterval);
56 commandManager->commandStatusChanged();
59 JucerDocument::~JucerDocument()
61 commandManager->commandStatusChanged();
64 //==============================================================================
65 void JucerDocument::changed()
67 FileBasedDocument::changed();
68 commandManager->commandStatusChanged();
71 void JucerDocument::timerCallback()
73 if ((lastFocusedComp != Component::getCurrentlyFocusedComponent()
74 || lastClickCounter != Desktop::getMouseButtonClickCounter())
75 && ! Component::isMouseButtonDownAnywhere())
77 lastFocusedComp = Component::getCurrentlyFocusedComponent();
78 lastClickCounter = Desktop::getMouseButtonClickCounter();
80 getUndoManager().beginNewTransaction();
84 bool JucerDocument::perform (UndoableAction* const action, const String& actionName)
86 startTimer (timerInterval);
87 return undoManager.perform (action, actionName);
90 void JucerDocument::refreshAllPropertyComps()
92 if (getComponentLayout() != 0)
93 getComponentLayout()->getSelectedSet().changed();
95 for (int i = getNumPaintRoutines(); --i >= 0;)
97 getPaintRoutine (i)->getSelectedElements().changed();
98 getPaintRoutine (i)->getSelectedPoints().changed();
102 //==============================================================================
103 void JucerDocument::setClassName (const String& newName)
105 if (newName != className
106 && makeValidCppIdentifier (newName, false, false, true).isNotEmpty())
108 className = makeValidCppIdentifier (newName, false, false, true);
109 changed();
113 void JucerDocument::setComponentName (const String& newName)
115 if (newName != componentName)
117 componentName = newName;
118 changed();
122 const String JucerDocument::getParentClassString() const
124 return parentClasses;
127 void JucerDocument::setParentClasses (const String& classes)
129 if (classes != parentClasses)
131 StringArray parentClassLines;
132 parentClassLines.addTokens (classes, ",", String::empty);
133 parentClassLines.trim();
134 parentClassLines.removeEmptyStrings();
135 parentClassLines.removeDuplicates (false);
137 for (int i = parentClassLines.size(); --i >= 0;)
139 String s (parentClassLines[i]);
140 String type;
142 if (s.startsWith ("public ")
143 || s.startsWith ("protected ")
144 || s.startsWith ("private "))
146 type = s.upToFirstOccurrenceOf (" ", true, false);
147 s = s.fromFirstOccurrenceOf (" ", false, false);
149 if (s.trim().isEmpty())
150 type = s = String::empty;
153 s = type + makeValidCppIdentifier (s.trim(), false, false, true);
155 parentClassLines.set (i, s);
158 parentClasses = parentClassLines.joinIntoString (", ");
159 changed();
163 const String JucerDocument::getConstructorParams() const
165 return constructorParams;
168 void JucerDocument::setConstructorParams (const String& newParams)
170 if (constructorParams != newParams)
172 constructorParams = newParams;
173 changed();
177 const String JucerDocument::getVariableInitialisers() const
179 return variableInitialisers;
182 void JucerDocument::setVariableInitialisers (const String& newInitlialisers)
184 if (variableInitialisers != newInitlialisers)
186 variableInitialisers = newInitlialisers;
187 changed();
191 void JucerDocument::setFixedSize (const bool isFixed)
193 if (fixedSize != isFixed)
195 fixedSize = isFixed;
196 changed();
200 void JucerDocument::setInitialSize (int w, int h)
202 w = jmax (1, w);
203 h = jmax (1, h);
205 if (initialWidth != w || initialHeight != h)
207 initialWidth = w;
208 initialHeight = h;
209 changed();
213 //==============================================================================
214 bool JucerDocument::isSnapActive (const bool disableIfCtrlKeyDown) const throw()
216 return snapActive != (disableIfCtrlKeyDown && ModifierKeys::getCurrentModifiers().isCtrlDown());
219 int JucerDocument::snapPosition (int pos) const throw()
221 if (isSnapActive (true))
223 jassert (snapGridPixels > 0);
224 pos = ((pos + snapGridPixels * 1024 + snapGridPixels / 2) / snapGridPixels - 1024) * snapGridPixels;
227 return pos;
230 void JucerDocument::setSnappingGrid (const int numPixels, const bool active, const bool shown)
232 if (numPixels != snapGridPixels
233 || active != snapActive
234 || shown != snapShown)
236 snapGridPixels = numPixels;
237 snapActive = active;
238 snapShown = shown;
239 changed();
244 //==============================================================================
245 void JucerDocument::setComponentOverlayOpacity (const float alpha)
247 if (alpha != componentOverlayOpacity)
249 componentOverlayOpacity = alpha;
250 changed();
254 //==============================================================================
255 const String JucerDocument::getDocumentTitle()
257 return className;
260 //==============================================================================
261 void JucerDocument::addMethod (const String& base, const String& returnVal, const String& method, const String& initialContent,
262 StringArray& baseClasses, StringArray& returnValues, StringArray& methods, StringArray& initialContents)
264 baseClasses.add (base);
265 returnValues.add (returnVal);
266 methods.add (method);
267 initialContents.add (initialContent);
270 void JucerDocument::getOptionalMethods (StringArray& baseClasses,
271 StringArray& returnValues,
272 StringArray& methods,
273 StringArray& initialContents) const
275 addMethod ("Component", "void", "visibilityChanged()", "", baseClasses, returnValues, methods, initialContents);
276 addMethod ("Component", "void", "moved()", "", baseClasses, returnValues, methods, initialContents);
277 addMethod ("Component", "void", "parentHierarchyChanged()", "", baseClasses, returnValues, methods, initialContents);
278 addMethod ("Component", "void", "parentSizeChanged()", "", baseClasses, returnValues, methods, initialContents);
279 addMethod ("Component", "void", "lookAndFeelChanged()", "", baseClasses, returnValues, methods, initialContents);
280 addMethod ("Component", "bool", "hitTest (int x, int y)", "return true;", baseClasses, returnValues, methods, initialContents);
281 addMethod ("Component", "void", "broughtToFront()", "", baseClasses, returnValues, methods, initialContents);
282 addMethod ("Component", "void", "filesDropped (const StringArray& filenames, int mouseX, int mouseY)", "", baseClasses, returnValues, methods, initialContents);
283 addMethod ("Component", "void", "handleCommandMessage (int commandId)", "", baseClasses, returnValues, methods, initialContents);
284 addMethod ("Component", "void", "childrenChanged()", "", baseClasses, returnValues, methods, initialContents);
285 addMethod ("Component", "void", "enablementChanged()", "", baseClasses, returnValues, methods, initialContents);
287 addMethod ("Component", "void", "mouseMove (const MouseEvent& e)", "", baseClasses, returnValues, methods, initialContents);
288 addMethod ("Component", "void", "mouseEnter (const MouseEvent& e)", "", baseClasses, returnValues, methods, initialContents);
289 addMethod ("Component", "void", "mouseExit (const MouseEvent& e)", "", baseClasses, returnValues, methods, initialContents);
290 addMethod ("Component", "void", "mouseDown (const MouseEvent& e)", "", baseClasses, returnValues, methods, initialContents);
291 addMethod ("Component", "void", "mouseDrag (const MouseEvent& e)", "", baseClasses, returnValues, methods, initialContents);
292 addMethod ("Component", "void", "mouseUp (const MouseEvent& e)", "", baseClasses, returnValues, methods, initialContents);
293 addMethod ("Component", "void", "mouseDoubleClick (const MouseEvent& e)", "", baseClasses, returnValues, methods, initialContents);
294 addMethod ("Component", "void", "mouseWheelMove (const MouseEvent& e, float wheelIncrementX, float wheelIncrementY)", "", baseClasses, returnValues, methods, initialContents);
296 addMethod ("Component", "bool", "keyPressed (const KeyPress& key)", "return false; // Return true if your handler uses this key event, or false to allow it to be passed-on.", baseClasses, returnValues, methods, initialContents);
297 addMethod ("Component", "bool", "keyStateChanged (const bool isKeyDown)", "return false; // Return true if your handler uses this key event, or false to allow it to be passed-on.", baseClasses, returnValues, methods, initialContents);
298 addMethod ("Component", "void", "modifierKeysChanged (const ModifierKeys& modifiers)", "", baseClasses, returnValues, methods, initialContents);
300 addMethod ("Component", "void", "focusGained (FocusChangeType cause)", "", baseClasses, returnValues, methods, initialContents);
301 addMethod ("Component", "void", "focusLost (FocusChangeType cause)", "", baseClasses, returnValues, methods, initialContents);
302 addMethod ("Component", "void", "focusOfChildComponentChanged (FocusChangeType cause)", "", baseClasses, returnValues, methods, initialContents);
303 addMethod ("Component", "void", "modifierKeysChanged (const ModifierKeys& modifiers)", "", baseClasses, returnValues, methods, initialContents);
304 addMethod ("Component", "void", "inputAttemptWhenModal()", "", baseClasses, returnValues, methods, initialContents);
307 void JucerDocument::setOptionalMethodEnabled (const String& methodSigniture, const bool enable)
309 if (enable)
310 activeExtraMethods.addIfNotAlreadyThere (methodSigniture);
311 else
312 activeExtraMethods.removeString (methodSigniture);
314 changed();
317 bool JucerDocument::isOptionalMethodEnabled (const String& methodSigniture) const throw()
319 return activeExtraMethods.contains (methodSigniture);
323 void JucerDocument::addExtraClassProperties (PropertyPanel* panel)
327 //==============================================================================
328 const char* const JucerDocument::jucerCompXmlTag = "JUCER_COMPONENT";
330 XmlElement* JucerDocument::createXml() const
332 XmlElement* doc = new XmlElement (jucerCompXmlTag);
334 doc->setAttribute ("documentType", getTypeName());
335 doc->setAttribute ("className", className);
336 doc->setAttribute ("componentName", componentName);
337 doc->setAttribute ("parentClasses", parentClasses);
338 doc->setAttribute ("constructorParams", constructorParams);
339 doc->setAttribute ("variableInitialisers", variableInitialisers);
340 doc->setAttribute ("snapPixels", snapGridPixels);
341 doc->setAttribute ("snapActive", snapActive);
342 doc->setAttribute ("snapShown", snapShown);
343 doc->setAttribute ("overlayOpacity", (double) componentOverlayOpacity);
344 doc->setAttribute ("fixedSize", fixedSize);
345 doc->setAttribute ("initialWidth", initialWidth);
346 doc->setAttribute ("initialHeight", initialHeight);
348 if (activeExtraMethods.size() > 0)
350 XmlElement* extraMethods = new XmlElement ("METHODS");
351 doc->addChildElement (extraMethods);
353 for (int i = 0; i < activeExtraMethods.size(); ++i)
355 XmlElement* e = new XmlElement ("METHOD");
356 extraMethods ->addChildElement (e);
357 e->setAttribute ("name", activeExtraMethods[i]);
361 return doc;
364 bool JucerDocument::loadFromXml (const XmlElement& xml)
366 if (xml.hasTagName (jucerCompXmlTag)
367 && getTypeName().equalsIgnoreCase (xml.getStringAttribute ("documentType", ObjectTypes::documentTypeNames[0])))
369 className = xml.getStringAttribute ("className", defaultClassName);
370 componentName = xml.getStringAttribute ("componentName", String::empty);
371 parentClasses = xml.getStringAttribute ("parentClasses", defaultParentClasses);
372 constructorParams = xml.getStringAttribute ("constructorParams", String::empty);
373 variableInitialisers = xml.getStringAttribute ("variableInitialisers", String::empty);
375 fixedSize = xml.getBoolAttribute ("fixedSize", false);
376 initialWidth = xml.getIntAttribute ("initialWidth", 300);
377 initialHeight = xml.getIntAttribute ("initialHeight", 200);
379 snapGridPixels = xml.getIntAttribute ("snapPixels", snapGridPixels);
380 snapActive = xml.getBoolAttribute ("snapActive", snapActive);
381 snapShown = xml.getBoolAttribute ("snapShown", snapShown);
383 componentOverlayOpacity = (float) xml.getDoubleAttribute ("overlayOpacity", 0.0);
385 activeExtraMethods.clear();
387 XmlElement* const methods = xml.getChildByName ("METHODS");
389 if (methods != 0)
391 forEachXmlChildElementWithTagName (*methods, e, "METHOD")
393 activeExtraMethods.addIfNotAlreadyThere (e->getStringAttribute ("name"));
397 activeExtraMethods.trim();
398 activeExtraMethods.removeEmptyStrings();
400 changed();
401 getUndoManager().clearUndoHistory();
402 return true;
405 return false;
408 //==============================================================================
409 const String JucerDocument::loadDocument (const File& file)
411 String error (TRANS("Not a valid Jucer cpp file"));
413 const File cppFile (file.withFileExtension (".cpp"));
415 const String cppFileString (cppFile.loadFileAsString());
417 resources.loadFromCpp (file, cppFileString);
419 XmlElement* const xml = pullMetaDataFromCppFile (cppFileString);
421 if (xml != 0)
423 if (loadFromXml (*xml))
424 error = String::empty;
425 else
426 error = TRANS("Couldn't parse the XML section of this file correctly");
428 delete xml;
431 return error;
434 const String JucerDocument::saveDocument (const File& file)
436 const File cppFile (file.withFileExtension (".cpp"));
437 const File hFile (file.withFileExtension (".h"));
439 String templateH, templateCpp;
441 if (! findTemplateFiles (templateH, templateCpp))
442 return TRANS("Couldn't find the required Jucer template files...\n\nMake sure the template files directory is set up correctly in the preferences box.");
444 const bool ok = writeCodeFiles (hFile, cppFile, templateH, templateCpp);
445 TestComponent::reloadAll();
447 if (ok)
448 return String::empty;
449 else
450 return TRANS("Couldn't write to the file.");
453 //==============================================================================
454 const File JucerDocument::getLastDocumentOpened()
456 return StoredSettings::getInstance()->recentFiles.getFile (0);
459 void JucerDocument::setLastDocumentOpened (const File& file)
461 StoredSettings::getInstance()->recentFiles.addFile (file);
464 //==============================================================================
465 XmlElement* JucerDocument::pullMetaDataFromCppFile (const String& cpp)
467 StringArray lines;
468 lines.addLines (cpp);
470 int startLine = indexOfLineStartingWith (lines, "BEGIN_JUCER_METADATA", 0);
472 if (startLine > 0)
474 int endLine = indexOfLineStartingWith (lines, "END_JUCER_METADATA", 0);
476 if (endLine > startLine)
478 String xmlText;
480 for (int i = startLine + 1; i < endLine; ++i)
481 xmlText << lines[i] << "\n";
483 XmlDocument doc (xmlText);
484 return doc.getDocumentElement();
488 return 0;
491 //==============================================================================
492 void JucerDocument::fillInGeneratedCode (GeneratedCode& code) const
494 code.className = className;
495 code.componentName = componentName;
496 code.parentClasses = parentClasses;
497 code.constructorParams = constructorParams;
498 code.initialisers.addLines (variableInitialisers);
500 if (! componentName.isEmpty())
501 code.parentClassInitialiser = "Component (" + quotedString (code.componentName) + ")";
503 // call these now, just to make sure they're the first two methods in the list.
504 code.getCallbackCode (String::empty, "void", "paint (Graphics& g)", false)
505 << "//[UserPrePaint] Add your own custom painting code here..\n//[/UserPrePaint]\n\n";
507 code.getCallbackCode (String::empty, "void", "resized()", false);
509 if (getComponentLayout() != 0)
510 getComponentLayout()->fillInGeneratedCode (code);
512 fillInPaintCode (code);
514 XmlElement* const e = createXml();
515 jassert (e != 0);
516 code.jucerMetadata = e->createDocument (String::empty, false, false);
517 delete e;
519 resources.fillInGeneratedCode (code);
521 code.constructorCode
522 << "\n//[UserPreSize]\n//[/UserPreSize]\n";
524 if (initialWidth > 0 || initialHeight > 0)
525 code.constructorCode
526 << "\nsetSize (" << initialWidth << ", " << initialHeight << ");\n";
528 code.getCallbackCode (String::empty, "void", "paint (Graphics& g)", false)
529 << "//[UserPaint] Add your own custom painting code here..\n//[/UserPaint]";
531 code.getCallbackCode (String::empty, "void", "resized()", false)
532 << "//[UserResized] Add your own custom resize handling here..\n//[/UserResized]";
534 // add optional methods
535 StringArray baseClasses, returnValues, methods, initialContents;
536 getOptionalMethods (baseClasses, returnValues, methods, initialContents);
538 for (int i = 0; i < methods.size(); ++i)
540 if (isOptionalMethodEnabled (methods[i]))
542 String& s = code.getCallbackCode (baseClasses[i], returnValues[i], methods[i], false);
544 if (! s.contains ("//["))
546 String userCommentTag ("UserCode_");
547 userCommentTag += methods[i].upToFirstOccurrenceOf ("(", false, false).trim();
549 s << "\n//["
550 << userCommentTag
551 << "] -- Add your code here...\n"
552 << initialContents[i];
554 if (initialContents[i].isNotEmpty() && ! initialContents[i].endsWithChar ('\n'))
555 s << '\n';
557 s << "//[/"
558 << userCommentTag
559 << "]\n";
565 void JucerDocument::fillInPaintCode (GeneratedCode& code) const
567 for (int i = 0; i < getNumPaintRoutines(); ++i)
569 getPaintRoutine (i)
570 ->fillInGeneratedCode (code, code.getCallbackCode (String::empty, "void", "paint (Graphics& g)", false));
574 //==============================================================================
575 bool JucerDocument::findTemplateFiles (String& templateH, String& templateCpp) const
577 const File templateDir (StoredSettings::getInstance()->getTemplatesDir());
579 const File hTemplate (templateDir.getChildFile ("jucer_ComponentTemplate.h"));
580 const File cppTemplate (templateDir.getChildFile ("jucer_ComponentTemplate.cpp"));
582 if (! (cppTemplate.existsAsFile() && hTemplate.existsAsFile()))
583 return false;
585 templateH = hTemplate.loadFileAsString();
586 templateCpp = cppTemplate.loadFileAsString();
588 const String jucerVersionString ("Jucer version: " + String (JUCER_MAJOR_VERSION)
589 + "." + String (JUCER_MINOR_VERSION));
591 // This checks the template files to see if they're the ones that shipped with this
592 // version of the jucer. If it fails, you're probably using the wrong ones.
593 // If you're using customised template files, just add the appropriate version line to
594 // their headers to avoid this warning.
595 jassert (templateH.containsIgnoreCase (jucerVersionString));
596 jassert (templateCpp.containsIgnoreCase (jucerVersionString));
598 return true;
601 void JucerDocument::getPreviewFiles (String& h, String& cpp)
603 if (! findTemplateFiles (h, cpp))
605 h = cpp = TRANS("Couldn't find the required Jucer template files...\n\nMake sure the template files directory is set up correctly in the preferences box.");
607 else
609 GeneratedCode generated (this);
610 fillInGeneratedCode (generated);
611 generated.includeFilesCPP.insert (0, getFile().withFileExtension ("h").getFileName());
613 generated.applyToCode (h, getClassName(), true);
614 generated.applyToCode (cpp, getClassName(), true);
618 static const String fixNewLines (const String& s)
620 StringArray lines;
621 lines.addLines (s);
623 for (int i = 0; i < lines.size(); ++i)
624 lines.set (i, lines[i].trimEnd());
626 while (lines.size() > 0 && lines [lines.size() - 1].trim().isEmpty())
627 lines.remove (lines.size() - 1);
629 return lines.joinIntoString ("\r\n") + "\r\n";
632 bool JucerDocument::writeCodeFiles (const File& headerFile,
633 const File& cppFile,
634 String h,
635 String cpp) const
637 GeneratedCode generated (this);
638 fillInGeneratedCode (generated);
640 generated.includeFilesCPP.insert (0, headerFile.getFileName());
642 String existingHeader (h), existingCpp (cpp);
644 if (headerFile.existsAsFile())
645 existingHeader = headerFile.loadFileAsString();
647 if (cppFile.existsAsFile())
648 existingCpp = cppFile.loadFileAsString();
650 generated.applyToCode (h, headerFile.getFileNameWithoutExtension(), false, existingHeader);
651 generated.applyToCode (cpp, headerFile.getFileNameWithoutExtension(), false, existingCpp);
653 h = fixNewLines (h);
654 cpp = fixNewLines (cpp);
656 return headerFile.replaceWithText (h, false, false)
657 && cppFile.replaceWithText (cpp, false, false);